package com.apes.fn.business.insurance.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fn.business.insurance.modle.InsurancePolicyFix;
import com.apes.fn.business.insurance.modle.InsurancePolicyFixItem;
import com.apes.fn.business.insurance.modle.PolicyInformation;
import com.apes.fn.business.insurance.modle.PolicyInformationItem;
import com.apes.fn.business.insurance.repository.InsurancePolicyFixRepository;
import com.apes.fn.business.insurance.repository.PolicyInformationItemRepository;
import com.apes.fn.business.insurance.repository.PolicyInformationRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.Arith;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.ReflectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: apes-server
 * @description
 * @author: czs
 * @create: 2022-02-10 11:56
 **/
@Service("insurancePolicyFixService")
public class InsurancePolicyFixService {

    @Autowired
    private InsurancePolicyFixRepository insurancePolicyFixRepository;

    @Autowired
    private PolicyInformationItemRepository policyInformationItemRepository;

    @Autowired
    private PolicyInformationRepository policyInformationRepository;

    public InsurancePolicyFix create(SimpleRequest request) {
        InsurancePolicyFix policyFix = request.getO(InsurancePolicyFix.class);

        filterUnselected(policyFix);
        lockPolicy(policyFix.getInsurancePolicyFixItems());
        return insurancePolicyFixRepository.saveAndFlush(policyFix);
    }

    private void filterUnselected(InsurancePolicyFix policyFix){
        Set<InsurancePolicyFixItem> insurancePolicyFixItems = policyFix.getInsurancePolicyFixItems();
        Set<InsurancePolicyFixItem> filter = insurancePolicyFixItems.stream().filter(insurancePolicyFixItem -> !insurancePolicyFixItem.isSelected()).collect(Collectors.toSet());
        unlockPolicy(filter);
        Set<InsurancePolicyFixItem> fixItems = insurancePolicyFixItems.stream().filter(InsurancePolicyFixItem::isSelected).collect(Collectors.toSet());
        if (fixItems.isEmpty())throw new RuntimeException("明细行不能为空");
        checkDistinct(fixItems);
        List<String> ids = fixItems.stream().map(InsurancePolicyFixItem::getPolicyNumber).collect(Collectors.toList());
        List<PolicyInformationItem> commissionFlag = policyInformationItemRepository.findAllBySettlementAndPolicyInformatioAndCommissionType(true,ids,policyFix.getCommissionType());
        if (!commissionFlag.isEmpty())throw new RuntimeException("保单号:"+commissionFlag.get(0).getId()+",已制定佣金对账单,如需修改,请先删除佣金对账单");
        policyFix.setInsurancePolicyFixItems(fixItems);
    }

    public void checkDistinct(Set<InsurancePolicyFixItem> insurancePolicyFixItems){
        Map<String,Integer> distinctMap = new HashMap();
        insurancePolicyFixItems.forEach(insurancePolicyFixItem -> {
            if (distinctMap.containsKey(insurancePolicyFixItem.getPolicyItemNumber())) {
                throw new RuntimeException("请检查,第 "+(distinctMap.get(insurancePolicyFixItem.getPolicyItemNumber())+1)+" 行与第 "+(insurancePolicyFixItem.getSort()+1)+" 行明细重复");
            }
            distinctMap.put(insurancePolicyFixItem.getPolicyItemNumber(),insurancePolicyFixItem.getSort());
        });
    }

    public InsurancePolicyFix update(SimpleRequest request) {
        InsurancePolicyFix policyFix = request.getO(InsurancePolicyFix.class);
        filterUnselected(policyFix);
        lockPolicy(policyFix.getInsurancePolicyFixItems());
        unlockPolicy(policyFix);
        return insurancePolicyFixRepository.saveAndFlush(policyFix);
    }

    private void unlockPolicy(InsurancePolicyFix policyFix) {
        InsurancePolicyFix old = insurancePolicyFixRepository.findOne(policyFix.getId());
        old.getInsurancePolicyFixItems().forEach(insurancePolicyFixItem -> {
            policyFix.getInsurancePolicyFixItems().forEach(insurancePolicyFixItem1 -> {
                boolean equals = insurancePolicyFixItem.getId().equals(insurancePolicyFixItem1.getId());
                if (equals)insurancePolicyFixItem.setDelete(false);
            });
        });
        Set<InsurancePolicyFixItem> insurancePolicyFixItems = old.getInsurancePolicyFixItems().stream().filter(InsurancePolicyFixItem::getDelete).collect(Collectors.toSet());
        unlockPolicy(insurancePolicyFixItems);
    }

    public InsurancePolicyFix delete(SimpleRequest request) {
        InsurancePolicyFix policyFix = request.getO(InsurancePolicyFix.class);
        policyFix.setState("delete");
        policyFix.setDeleteId(request.getPerson());
        unlockPolicy(policyFix.getInsurancePolicyFixItems());
        return insurancePolicyFixRepository.saveAndFlush(policyFix);
    }

    public InsurancePolicyFix save(SimpleRequest request) {
        InsurancePolicyFix policyFix = request.getO(InsurancePolicyFix.class);
        policyFix.setState("save");
        policyFix.setSaveId(request.getPerson());
        policyFix.setApproveDate(new Date()); // 记录审核时间
        // updatePolicy(policyFix);
        saveXGPolicy(policyFix);
        unlockPolicy(policyFix.getInsurancePolicyFixItems());
        return insurancePolicyFixRepository.saveAndFlush(policyFix);
    }

    public void lockPolicy(Set<InsurancePolicyFixItem> insurancePolicyFixItems){
        List<String> ids = insurancePolicyFixItems.stream().map(InsurancePolicyFixItem::getPolicyItemNumber).collect(Collectors.toList());
        if (!ids.isEmpty())policyInformationItemRepository.updatePolicyInformationItemFixStateToOne(ids);
    }

    public void unlockPolicy(Set<InsurancePolicyFixItem> insurancePolicyFixItems){
        List<String> ids = insurancePolicyFixItems.stream().map(InsurancePolicyFixItem::getPolicyItemNumber).collect(Collectors.toList());
        if (!ids.isEmpty())policyInformationItemRepository.updatePolicyInformationItemFixStateToNull(ids);
    }

    public void updatePolicy(InsurancePolicyFix policyFix){
        List<String> ids = policyFix.getInsurancePolicyFixItems().stream().map(InsurancePolicyFixItem::getPolicyItemNumber).collect(Collectors.toList());
//        List<PolicyInformation> orderSaveList = new ArrayList<>();
        List<PolicyInformationItem> itemSaveList = new ArrayList<>();
        Map<String, List<PolicyInformationItem>> Items = policyInformationItemRepository.findByIds(ids).stream().collect(Collectors.groupingBy(PolicyInformationItem::getId));
        policyFix.getInsurancePolicyFixItems().forEach(insurancePolicyFixItem -> {

            PolicyInformationItem policyInformationItem = Items.get(insurancePolicyFixItem.getPolicyItemNumber()).get(0);
            policyInformationItem.setCommissionRatio(insurancePolicyFixItem.getFixCommissionRatio());
            policyInformationItem.setCommission(insurancePolicyFixItem.getFixCommission());
//            policyInformationItemRepository.saveAndFlush(policyInformationItem);
            itemSaveList.add(policyInformationItem);

//            PolicyInformation policyInformation = policyInformationItem.getPolicyInformation();
//            policyInformation.setInsuranceAmount(insurancePolicyFixItem.getInsuranceAmount());
//            policyInformationRepository.saveAndFlush(policyInformation);
//            orderSaveList.add(policyInformation);
        });

        policyInformationItemRepository.saveAll(itemSaveList);
//        policyInformationRepository.save(orderSaveList);
//        policyInformationRepository.saveAll(orderSaveList);
    }

    /**
     * 新增修改保单
     * @param policyFix
     */
    public void saveXGPolicy(InsurancePolicyFix policyFix){
        List<String> ids = policyFix.getInsurancePolicyFixItems().stream()
                .map(InsurancePolicyFixItem::getPolicyItemNumber).collect(Collectors.toList());

        Map<String, List<PolicyInformationItem>> oldInfoItems = policyInformationItemRepository.findByIds(ids).stream()
                .collect(Collectors.groupingBy(PolicyInformationItem::getId));

        policyFix.getInsurancePolicyFixItems().stream()
            .forEach(
                policyFixItem -> {
                PolicyInformationItem oldInfoItem = oldInfoItems.get(policyFixItem.getPolicyItemNumber()).get(0);
                // 新增修改保单号
                PolicyInformation policyInformation = policyInformationRepository.findById(oldInfoItem.getPolicyInformation().getId()).get();
                PolicyInformation policyInformationXG = policyInformationXG(policyInformation);
                // 新增修改保单明细
                Set<PolicyInformationItem> policyInformationItems = new HashSet();
                policyInformationItems.add(savePolicyInformationItemXG(oldInfoItem,policyFixItem,policyInformationXG));
                policyInformationXG.setPolicyInformationItems(policyInformationItems);
                policyInformationRepository.saveAndFlush(policyInformationXG);
            });
    }

    /**
     * 新增修改保单号
     * @param policyInformation
     * @return
     */
    public PolicyInformation policyInformationXG(PolicyInformation policyInformation){
        PolicyInformation policyInformationXG = new PolicyInformation();
        ReflectUtil.copyAll(policyInformation, policyInformationXG, "id", "policyInformationItems", "fnProfitRatio", "fnProfit", "totalPercent", "commissionTotalAmount", "allowancePercent", "allowanceAmount");
        // 修改保单号生成逻辑： 原保单号 + "-XG-" + 【该保单被修改次数】
        int count = policyInformationRepository.findByXG(policyInformation.getId()).size();
        policyInformationXG.setSourceBusinessType(PolicyInformation.CONST_SOURCE_FORM_FIX);
        policyInformationXG.setId(policyInformation.getId() + "-XG-" + (++count));
        return policyInformationXG;
    }

    /**
     * 新增修改保单明细
     * @param oldInfoItem
     * @param policyFixItem
     * @param policyInformationXG
     * @return
     */
    public PolicyInformationItem savePolicyInformationItemXG(PolicyInformationItem oldInfoItem,InsurancePolicyFixItem policyFixItem,PolicyInformation policyInformationXG){
        PolicyInformationItem policyInformationItemXG = new PolicyInformationItem();
        ReflectUtil.copyAll(oldInfoItem, policyInformationItemXG, "id");
        // 计算佣金比率
        double commissionRatio = Arith.sub(policyFixItem.getFixCommissionRatio(), oldInfoItem.getCommissionRatio());
        // 计算佣金金额
        double commission = commissionRatio == 0 ? 0 : Arith.sub(policyFixItem.getFixCommission(), oldInfoItem.getCommission());
        // 计算赋能佣金比率
        double fnCommissionRatio = Arith.sub(policyFixItem.getFixFnCommissionRatio(), oldInfoItem.getFnCommissionRatio());
        // 计算赋能佣金金额
        double fnCommission = fnCommissionRatio == 0 ? 0 : Arith.sub(policyFixItem.getFixFnCommissionAmount(), oldInfoItem.getFnCommission());
        // 赋值
        policyInformationItemXG.setCommissionRatio(commissionRatio);
        policyInformationItemXG.setCommission(commission);
        policyInformationItemXG.setFnCommission(fnCommission);
        policyInformationItemXG.setFnCommissionRatio(fnCommissionRatio);
        // 关联修改保单主表
        policyInformationItemXG.setPolicyInformation(policyInformationXG);
        return policyInformationItemXG;
    }

    /**
     * 功能：用于查找以及查询使用
     *
     * @param request
     * @param
     * @return
     */
    public Page findAllPolicyInformationItem(SimpleRequest request) {
        JSONObject jo = request.getJO();
        boolean condition = jo.containsKey("condition");
        List<Map> filters =condition?request.get("condition"):new ArrayList<>();
        JSONArray jsonArray = new JSONArray();
        if (jo.getJSONObject("group")!=null)jsonArray.addAll(jo.getJSONObject("group").getJSONArray("filters"));
        if (jo.getJSONObject("domain")!=null)jsonArray.addAll(jo.getJSONObject("domain").getJSONArray("filters"));
        for (Object o : jsonArray) {
            JSONObject object = (JSONObject) o;
            filters.add(Objects.requireNonNull(MapUtil.mapped(object)));
        }
        addCondition(filters);
        Sort sort = new Sort(Sort.Direction.DESC, "createDate");
        Page<PolicyInformationItem> policyInformationItems = policyInformationItemRepository.findAll(JpaDsl.toCriteria(filters), PageRequest.of(request.get("page"), request.get("size"), sort));
        List<Map> result = policyInformationItems.getContent().stream().map(policyInformationItem -> {
            Map mapped = MapUtil.mapped(policyInformationItem);
            mapped.putAll(MapUtil.mapped(policyInformationItem.getPolicyInformation()));
            mapped.put("itemId",policyInformationItem.getId());
            return mapped;
        }).collect(Collectors.toList());
        return pageInfo(jo,result,policyInformationItems.getTotalElements());
    }

    public void addCondition(List<Map> filters){
        filters.add(MapUtil.mapper("field","fixState",
                "value",null,
                "operator","EQ"));
        filters.add(MapUtil.mapper("field","settlement",
                "value",0,
                "operator","EQ"));
    }

    /**
     * 手动分页
     * @param jo
     * @param cross
     * @return
     */
    public PageImpl pageInfo(JSONObject jo, List cross, long totalElements){
        int fixNum = 0;
        int size = ((Integer) jo.get("page")-fixNum)*(Integer) jo.get("size");

        Pageable pageable = PageRequest.of(((Integer) jo.get("page")), (Integer) jo.get("size"));
        PageImpl page = new PageImpl(cross, pageable, totalElements);
        return page;
    }

    /**
     * 导入有效保单
     *
     */
    public JSONObject importPolicy(SimpleRequest request){
        //Excel信息
        JSONObject importData = request.get("importData");
        JSONArray field =  request.get("data");
        JSONObject model = request.get("model");
        JSONArray itemArray = model.getJSONArray("insurancePolicyFixItems");
        //返回值
        JSONObject exclObject = new JSONObject();
        //行内容
        JSONArray exclArr = new JSONArray();
        //头部
        JSONArray headerName = new JSONArray();
        //field
        for (Object o : field) {
            JSONObject webTab = (JSONObject) o;
            headerName.add(webTab.get("name"));
        }
        if (model.getJSONObject("commissionType")==null)throw new RuntimeException("请先选择佣金类型");
        String commissionType = model.getJSONObject("commissionType").getString("name");
        Map<String,Integer> headerIndex = new HashMap<>();
        importData.forEach((k, v) -> {

            //获得value ===Excel内容
            JSONArray excl = (JSONArray) v;
            //可能会有多个Sheet 需要判断 sheet 里是否有数据
            if (excl.size()==0 || excl.size() == 1) {
                return;
            }
            //首行数据
            JSONArray header = (JSONArray) excl.get(0);
            //提取保单号和佣金类型
            for (int i = 0; i < header.size(); i++) {
                headerIndex.put(header.getString(i),i);
            }
//            if (headerIndex.size()<2)throw new RuntimeException("请确认Excel中存在\"保单号\"与\"佣金类型\"列");
            if (!headerIndex.containsKey("保单号") || !headerIndex.containsKey("佣金类型")) throw new RuntimeException("请确认Excel中存在\"保单号\"与\"佣金类型\"列");
            //遍历每一行
            for (int i = 0; i < excl.size(); i++) {
                //跳过首行
                if (i == 0) {
                    exclArr.add(headerName);
                    continue;
                }
                //当前行
                JSONArray lineLong = (JSONArray) excl.get(i);
                // 当前行后面列为空会导致长度不一致
                int size = ((JSONArray) excl.get(0)).size();
                if (lineLong.size() < size){
                    while (lineLong.size() < size){
                        lineLong.add(null);
                    }
                }
                JSONArray returnData = new JSONArray();
                Map<String,Object> data = new HashMap<>();
                if (lineLong.size()<=0)continue;
                String comType = getStringByHeader(lineLong, headerIndex, "佣金类型");
                if (!commissionType.equals(comType)) {
                    continue;
                }
                String policyNumber = getStringByHeader(lineLong, headerIndex, "保单号");
                PolicyInformationItem policyInformationItem = policyInformationItemRepository.findByPolicyAndCommissionType(policyNumber, comType);
                if (policyInformationItem==null)throw new RuntimeException("保单号:"+policyNumber+",未查询到对于保单信息");
                if (policyInformationItem.isSettlement())throw new RuntimeException("保单号:"+policyInformationItem.getId()+",已制定佣金对账单,如需修改,请先删除佣金对账单");
                if (policyInformationItem.getFixState()!=null)throw new RuntimeException(policyInformationItem.getId()+",该保单明细正在修改!!");
                Map mapped = MapUtil.mapped(policyInformationItem);
                PolicyInformation policyInformation = policyInformationItem.getPolicyInformation();
                mapped.putAll(MapUtil.mapped(policyInformation));
                mapped.put("itemId",policyInformationItem.getId());
                // 计算保险公司修改/赋能修改比率与金额
                double fixCommissionRatio = getDoubleByHeader(lineLong, headerIndex, "保险公司修改后比率");
                double fixFnCommissionRatio = getDoubleByHeader(lineLong, headerIndex, "赋能修改后比率");
                fixCommissionRatio = fixCommissionRatio == 0 ? policyInformationItem.getCommissionRatio() : fixCommissionRatio;
                fixFnCommissionRatio = fixFnCommissionRatio == 0 ? policyInformationItem.getFnCommissionRatio() : fixFnCommissionRatio;
                double fixCommission = Arith.round(Arith.mul(policyInformation.getNetPremium(),fixCommissionRatio),2);
                double fixFnCommissionAmount = Arith.round(Arith.mul(policyInformation.getInsuranceAmount(),fixFnCommissionRatio),2);

                data.put("保单号",policyNumber);
                data.put("保险公司", policyInformation.getSupplier().getName());
                data.put("险种类别", policyInformation.getInsuranceType());
                data.put("保费金额", policyInformation.getInsuranceAmount());
                data.put("净保费", policyInformation.getNetPremium());
                data.put("佣金类型", comType);
                data.put("原佣金比率", policyInformationItem.getCommissionRatio());
                data.put("原佣金金额", policyInformationItem.getCommission());
                data.put("保险公司修改后比率", fixCommissionRatio);
                data.put("修改后金额", fixCommission);
                data.put("赋能修改后比率", fixFnCommissionRatio);
                data.put("赋能修改后金额", fixFnCommissionAmount);
                data.put("原赋能佣金比率", policyInformationItem.getFnCommissionRatio());
                data.put("原赋能佣金金额", policyInformationItem.getFnCommission());

                intsertReturnData(headerName,data,returnData);
                returnData.remove(2);
                returnData.add(3,mapped);
                returnData.add(0,"0");
                returnData.add(0,"0");
                exclArr.add(returnData);
            }
            exclObject.put(k, exclArr);
        });


        return exclObject;
    }

    private void intsertReturnData(JSONArray headerName,Map<String,Object> data,JSONArray returnData){
        for (Object o : headerName) {
            String name = (String) o;
            returnData.add(data.getOrDefault(name, "0"));
        }
    }

    private String getStringByHeader(JSONArray lineLong,Map<String,Integer> headerIndex,String headerName){
        if (!headerIndex.containsKey(headerName)) return null;
        Integer index = headerIndex.get(headerName);
        if (lineLong.size() < index) return null;
        return lineLong.getString(index);
    }

    private double getDoubleByHeader(JSONArray lineLong,Map<String,Integer> headerIndex,String headerName){
        if (!headerIndex.containsKey(headerName)) return 0;
        Integer index = headerIndex.get(headerName);
        if (lineLong.size() < index) return 0;
        return lineLong.getDouble(index) == null ? 0 : lineLong.getDouble(index);
    }
}
